package web

import (
	"github.com/dlclark/regexp2"
	"github.com/gin-contrib/pprof"
	"github.com/gin-contrib/sessions"
	"github.com/gin-gonic/gin"
	"github.com/golang-jwt/jwt/v5"
	"go.uber.org/zap"
	"jk-time/webook/internal/domain"
	"jk-time/webook/internal/service"
	ijwt "jk-time/webook/internal/web/jwt"
	"jk-time/webook/pkg/ginx"
	"log"
	"net/http"
)

type UserHandler struct {
	emailExp    *regexp2.Regexp
	passwordExp *regexp2.Regexp
	userSrv     service.UserService
	codeSvc     service.CodeService
	ijwt.RedisJWTHandler
}

const (
	bizLogin = "login"
)

func NewUserHandler(userSrv service.UserService, codeSvc service.CodeService) *UserHandler {
	const (
		passwordPattern = `^.{3,18}$`
		emailPattern    = `^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$`
	)
	//密码预编译
	emailExp := regexp2.MustCompile(emailPattern, regexp2.None)
	passwordExp := regexp2.MustCompile(passwordPattern, regexp2.None)
	return &UserHandler{
		passwordExp: passwordExp,
		emailExp:    emailExp,
		userSrv:     userSrv,
		codeSvc:     codeSvc,
	}
}
func (u *UserHandler) RegisterRouters(e *gin.Engine) {
	rg := e.Group("/users")
	rg.POST("/signup", u.Signup)
	rg.POST("/login", u.LoginJWT)
	rg.POST("/logout", u.LogoutJWT)
	rg.POST("/edit", u.Edit)
	rg.POST("/profile", ginx.WrapBodyAndToken[LoginReq, jwt.Claims](u.Profile))
	rg.POST("/login_sms/code/send", u.SendLoginSMSCode)
	rg.POST("/login_sms", u.LoginSMS)
	rg.POST("/refresh_token", u.RefreshToken)
	pprof.Register(rg)
}
func (u *UserHandler) SendLoginSMSCode(ctx *gin.Context) {
	type Req struct {
		Phone string `json:"phone"`
	}
	var req Req
	//解析错了，写回400的错误
	if err := ctx.BindJSON(&req); err != nil {
		zap.L().Info("反序列化请求失败:", zap.Error(err))
		return
	}
	if req.Phone == "" {
		ctx.JSON(http.StatusOK, Result{
			Code: 4,
			Msg:  "请输入手机号码",
		})
		return
	}
	err := u.codeSvc.Send(ctx, req.Phone, bizLogin)
	switch err {
	case nil:
		ctx.JSON(http.StatusOK, Result{
			Msg: "发送成功",
		})
	case service.ErrCodeSendTooMany:
		ctx.JSON(http.StatusOK, Result{
			Code: 4,
			Msg:  "短信发送太频繁，请稍后再试",
		})
	default:
		ctx.JSON(http.StatusOK, Result{
			Code: 5,
			Msg:  "系统错误",
		})
		log.Println(err)
	}
}

// LoginSMS
/**
 * 1.绑定校验参数
 * 2.验证码校验
 * 3.发现/创建用户
 * 4.生成token返回
 */
func (u *UserHandler) LoginSMS(ctx *gin.Context) {
	type Req struct {
		Phone string `json:"phone"`
		Code  string `json:"code"`
	}
	var req Req
	//解析错了，写回400的错误
	if err := ctx.BindJSON(&req); err != nil {
		zap.L().Info("反序列化请求失败:", zap.Error(err))
		return
	}

	ok, err := u.codeSvc.Varify(ctx, bizLogin, req.Code, req.Phone)
	if err != nil {
		ctx.JSON(http.StatusOK, Result{
			Code: 5,
			Msg:  "系统异常",
		})
		return
	}
	if !ok {
		ctx.JSON(http.StatusOK, Result{
			Code: 4,
			Msg:  "验证码不对，请重新输入",
		})
		return
	}
	//找或注册
	user, err := u.userSrv.FindOrCreate(ctx, req.Phone)
	if err != nil {
		ctx.JSON(http.StatusOK, Result{
			Code: 5,
			Msg:  "系统异常",
		})
		return
	}
	if err := u.SetLoginToken(ctx, user.Id); err != nil {
		ctx.JSON(http.StatusOK, Result{
			Code: 5,
			Msg:  "系统异常",
		})
		return
	}
	ctx.JSON(http.StatusOK, Result{
		Msg: "登录成功",
	})
}
func (u *UserHandler) Signup(ctx *gin.Context) {
	type SignUpReq struct {
		Email           string `json:"email" form:"email"`
		ConfirmPassword string `json:"confirmPassword" form:"confirmPassword"`
		Password        string `json:"password" form:"password"`
	}
	var req SignUpReq
	//解析错了，写回400的错误
	if err := ctx.BindJSON(&req); err != nil {
		zap.L().Info("反序列化请求失败:", zap.Error(err))
		return
	}

	ok, err := u.emailExp.MatchString(req.Email)
	if err != nil {
		ctx.String(http.StatusOK, "系统错误")
		zap.L().Error("邮件格式错误:", zap.Error(err))
		return
	}
	if !ok {
		ctx.String(http.StatusOK, "你的邮箱格式不对")
		return
	}
	if req.ConfirmPassword != req.Password {
		ctx.String(http.StatusOK, "两次输入的密码不一致")
		return
	}
	ok, err = u.passwordExp.MatchString(req.Password)
	if err != nil {
		ctx.String(http.StatusOK, "系统错误")
		return
	}
	if !ok {
		ctx.String(http.StatusOK, "你的密码格式不对")
		return
	}

	err = u.userSrv.SignUp(ctx, domain.User{
		Email:    req.Email,
		Password: req.Password,
	})
	switch err {
	case nil:
		ctx.String(http.StatusOK, "注册成功")
	case service.ErrDuplicateUser:
		ctx.String(http.StatusOK, "邮箱冲突，请换一个")
	default:
		ctx.String(http.StatusOK, "系统错误")
	}
}

// Login
/**
 * 1.绑定校验参数
 * 2.密码核对
 * 3.生成session
 */
func (u *UserHandler) Login(ctx *gin.Context) {
	var req LoginReq
	//解析错了，写回400的错误
	if err := ctx.BindJSON(&req); err != nil {
		zap.L().Info("反序列化请求失败:", zap.Error(err))
		return
	}
	user, err := u.userSrv.Login(ctx, req.Email, req.Password)
	switch err {
	case nil:
		//设置session
		sess := sessions.Default(ctx)
		sess.Set("userId", user.Id)
		sess.Options(sessions.Options{
			MaxAge: 60,
		})
		//保存
		err = sess.Save()
		if err != nil {
			zap.L().Error("session 保存失败:", zap.Error(err))
			ctx.String(http.StatusOK, "系统错误")
			return
		}
		ctx.String(http.StatusOK, "登录成功")
	case service.ErrDuplicateUser:
		ctx.String(http.StatusOK, "邮箱或者密码不对")
	default:
		ctx.String(http.StatusOK, "系统错误")
	}
}
func (u *UserHandler) LoginJWT(ctx *gin.Context) {
	var req LoginReq
	//解析错了，写回400的错误
	if err := ctx.BindJSON(&req); err != nil {
		zap.L().Info("反序列化请求失败:", zap.Error(err))
		return
	}
	user, err := u.userSrv.Login(ctx, req.Email, req.Password)
	switch err {
	case nil:
		if err := u.SetLoginToken(ctx, user.Id); err != nil {
			ctx.String(http.StatusOK, "系统错误")
			zap.L().Error("生成token失败:", zap.Error(err))
			return
		}
		ctx.String(http.StatusOK, "登录成功")
	case service.ErrInvalidUserOrPassword:
		ctx.String(http.StatusOK, "邮箱或者密码不对")
	default:
		ctx.String(http.StatusOK, "系统错误")
	}
}

//	func (u *UserHandler) Logout(ctx *gin.Context) {
//		sess := sessions.Default(ctx)
//		sess.Options(&sessions.Options{
//			MaxAge: -1,
//		})
//		sess.Save()
//	}
func (u *UserHandler) LogoutJWT(ctx *gin.Context) {
	err := u.ClearToken(ctx)
	if err != nil {
		ctx.String(http.StatusOK, "退出登录失败")
		zap.L().Error("清理token失败:", zap.Error(err))
		return
	}
	ctx.String(http.StatusOK, "退出登录成功")
}
func (u *UserHandler) Edit(ctx *gin.Context) {

}
func (u *UserHandler) Profile(ctx *gin.Context, req LoginReq, uc jwt.Claims) (ginx.Result, error) {
	return Result{
		Code: 200,
		Data: "niao",
	}, nil
}
func (u *UserHandler) RefreshToken(ctx *gin.Context) {
	tokenStr := u.ExtractToken(ctx)
	var rf ijwt.RefreshClaims
	token, err := jwt.ParseWithClaims(tokenStr, &rf, func(token *jwt.Token) (interface{}, error) {
		return []byte(ijwt.RtKey), nil
	})
	if err != nil || !token.Valid {
		ctx.AbortWithStatus(http.StatusUnauthorized)
		zap.L().Error("token 出错:", zap.Error(err))
		return
	}

	//存在已经退出的ssid
	cnt := u.CheckSession(ctx, rf.Ssid)
	if cnt > 0 {
		ctx.AbortWithStatus(http.StatusUnauthorized)
		zap.L().Error("该用户已退出")
		return
	}

	err = u.SetJWTToken(ctx, rf.Uid, rf.Ssid)
	if err != nil {
		ctx.JSON(http.StatusOK, Result{
			Code: 5,
			Msg:  "系统错误",
		})
		zap.L().Error("生成token失败:", zap.Error(err))
		return
	}
	ctx.JSON(http.StatusOK, Result{Msg: "刷新成功"})
}

type LoginReq struct {
	Email    string `json:"email" form:"email"`
	Password string `json:"password" form:"password"`
}
